package com.bicon.botu.medical.service.impl

import com.baomidou.mybatisplus.mapper.EntityWrapper
import com.baomidou.mybatisplus.service.impl.ServiceImpl
import com.bicon.botu.core.base.common.BusinessException
import com.bicon.botu.core.base.common.utils.DateUtil
import com.bicon.botu.medical.common.constant.Constant
import com.bicon.botu.medical.dao.mapper.UserHealthDataMapper
import com.bicon.botu.medical.model.dto.UserHealthDataDTO
import com.bicon.botu.medical.model.entity.HealthExp
import com.bicon.botu.medical.model.entity.User
import com.bicon.botu.medical.model.entity.UserHealthData
import com.bicon.botu.medical.model.vo.HealthScoreVO
import com.bicon.botu.medical.param.EntryHealthParam
import com.bicon.botu.medical.service.IHealthExpService
import com.bicon.botu.medical.service.IUserHealthDataService
import com.bicon.botu.medical.service.IUserService
import commonx.core.content.transferEntries
import commonx.core.date.calculateAge
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.math.BigDecimal
import java.math.RoundingMode
import java.util.*
import kotlin.math.absoluteValue
import kotlin.math.pow
import kotlin.math.roundToInt

/**
 * 体征数据业务接口 -- 实现类
 *
 * @author 刘小鹏
 * @date 2018/07/17
 */
@Service
open class UserHealthDataServiceImpl : ServiceImpl<UserHealthDataMapper, UserHealthData>(), IUserHealthDataService {

    @Autowired
    private lateinit var userService: IUserService
    @Autowired
    private lateinit var healthExpService: IHealthExpService

    @Transactional(rollbackFor = [(Throwable::class)])
    override fun saveHealthData(entryHealthParam: EntryHealthParam): HealthScoreVO? {
        var healthScoreVO: HealthScoreVO? = null
        /*
         更新用户信息
          */
        val u = userService.selectById(entryHealthParam.userId) ?: throw BusinessException("该用户不存在")
        val user = u.apply {
            id = entryHealthParam.userId
            nick = entryHealthParam.nick
            birthday = entryHealthParam.birthday
            gender = entryHealthParam.gender
            preUpdate()
        }
        entryHealthParam.healthDataList?.forEach {
            if (it.indexCode == Constant.WEIGHT) user.weight = it.indexValue?.toDouble() else if (it.indexCode == Constant.HEIGHT) user.height = it.indexValue?.toDouble()
            it.userId = entryHealthParam.userId
            it.preInsert()
        }
        user.run {
            // 初次保存体征综合分数
            var healthExpEntity = healthExpService.selectOne(EntityWrapper<HealthExp>().where("user_id={0}", entryHealthParam.userId))
            if (healthExpEntity == null) {
                healthExpEntity = HealthExp().apply {
                    userId = entryHealthParam.userId
                    preInsert()
                }
            }else{
                preUpdate()
            }
            healthExpEntity.healthExp = calculationHealthScore(weight, height, birthday) ?: 60
            val bmi = calculationBMI(weight, height).toBigDecimal().setScale(1, RoundingMode.HALF_UP).toDouble()
            val bodyFatRate = calculationBodyFatRate(weight, height, birthday, gender)
            healthScoreVO = HealthScoreVO(weight = weight, height = height, healthScore = healthExpEntity.healthExp, bodyFatRate = bodyFatRate, bmi = bmi)
            val insert = healthExpService.insertOrUpdate(healthExpEntity)
            if (!insert) {
                throw BusinessException("保存健康评分失败")
            }
        }

        val updateUser = userService.updateById(user)
        if (!updateUser) {
            throw BusinessException("修改用户体重和身高失败")
        }

        /*
         保存或修改身体指标数据
          */
        var healthDataLIst = this.selectList(EntityWrapper<UserHealthData>().where("user_id={0}", entryHealthParam.userId))
        if (healthDataLIst.isEmpty()) {
            healthDataLIst = entryHealthParam.healthDataList?.transferEntries<UserHealthData>()?.toList() ?: ArrayList()
        } else {
            healthDataLIst.forEach { healthData ->
                val h = entryHealthParam.healthDataList?.parallelStream()?.filter { it.indexName == healthData.indexName }?.findFirst()?.orElseGet { UserHealthDataDTO() }
                healthData.indexValue = h?.indexValue
                healthData.preUpdate()
            }
        }
        if (healthDataLIst.isNotEmpty()) {
            val insertBatch = this.insertOrUpdateBatch(healthDataLIst)
            if (!insertBatch) {
                throw BusinessException("批量保存用户健康数据失败")
            }
        }
        return healthScoreVO
    }

    /**
     * 计算健康评分
     *
     * @param weight 体重
     * @param height 身高
     * @param birthday 出生日期
     */
    private fun calculationHealthScore(weight: Double?, height: Double?, birthday: String?): Int? {
        val bmi = calculationBMI(weight, height)
        val age = DateUtil.StringToDate(birthday).calculateAge()
        val ageInterference = (age - 25).div(5).absoluteValue
        val div = bmi?.minus(21.2)?.absoluteValue?.div(21.2)
        val score = div.minus(1).absoluteValue.times(100).minus(ageInterference)?.absoluteValue
        return if (score.compareTo(60) == -1) 60 else score.roundToInt()
    }

    /**
     * 计算体脂率
     *
     * @param weight 体重
     * @param height 身高
     * @param birthday 出生日期
     * @param gender 性别（1：男; 0:女）
     */
    private fun calculationBodyFatRate(weight: Double?, height: Double?, birthday: String?, gender: Int?): Double? {
        val bmi = calculationBMI(weight, height)
        val age = DateUtil.StringToDate(birthday).calculateAge()
        val i = if (gender == 1) 1 else 0
        val b = 1.2.times(bmi).plus(0.23.times(age)).minus(5.4).minus(10.8.times(i))

        return BigDecimal(b).setScale(1, RoundingMode.HALF_UP).toDouble()
    }

    /**
     * 计算BMI指数（身体质量指数--判断身体胖瘦的指标）
     *
     * @param weight 体重
     * @param height 身高
     */
    private fun calculationBMI(weight: Double?, height: Double?): Double {
        return (height?.div(100))?.pow(2)?.let { weight?.div(it) } ?: 0.0
    }
}
